﻿using System;
using System.Text.RegularExpressions;

namespace Sencha.Serialization.Bson
{
	/// <summary>
	/// Representation of JS Regular Expressions in BSON. This type is immutable by design
	/// </summary>
	[TypeSerializer(typeof(Serializers.BsonRegexSerializer))]
	public sealed class BsonRegex : IEquatable<BsonRegex>
	{
		private string m_pattern;
		private string m_optionsString;

		public string Pattern { get { return m_pattern; } }
		public string Options { get { return m_optionsString; } }

		public BsonRegex()
		{
			m_pattern = string.Empty;
			m_optionsString = string.Empty;
		}
		public BsonRegex(string pattern, string optionsString)
		{
			if (pattern == null)
				throw new ArgumentNullException("pattern");
			if (optionsString == null)
				throw new ArgumentNullException("optionsString");

			m_pattern = pattern;
			m_optionsString = optionsString;
		}
		public BsonRegex(Regex regex)
		{
			if (regex == null)
				throw new ArgumentNullException("regex");

			var options = new System.Text.StringBuilder(7);
			if ((regex.Options & RegexOptions.ECMAScript) != 0)
				options.Append("e");
			if ((regex.Options & RegexOptions.IgnoreCase) != 0)
				options.Append("i");
			if ((regex.Options & RegexOptions.CultureInvariant) != 0)
				options.Append("l");
			if ((regex.Options & RegexOptions.Multiline) != 0)
				options.Append("m");
			if ((regex.Options & RegexOptions.Singleline) != 0)
				options.Append("s");
			if ((regex.Options & RegexOptions.IgnorePatternWhitespace) != 0)
				options.Append("w");
			if ((regex.Options & RegexOptions.ExplicitCapture) != 0)
				options.Append("x");

			m_pattern = regex.ToString();
			m_optionsString = options.ToString();
		}

		public Regex ToRegex()
		{
			var options = RegexOptions.None;
			if (m_optionsString.IndexOf('e') != -1) options = options | RegexOptions.ECMAScript;
			if (m_optionsString.IndexOf('i') != -1) options = options | RegexOptions.IgnoreCase;
			if (m_optionsString.IndexOf('l') != -1) options = options | RegexOptions.CultureInvariant;
			if (m_optionsString.IndexOf('m') != -1) options = options | RegexOptions.Multiline;
			if (m_optionsString.IndexOf('s') != -1) options = options | RegexOptions.Singleline;
			if (m_optionsString.IndexOf('w') != -1) options = options | RegexOptions.IgnorePatternWhitespace;
			if (m_optionsString.IndexOf('x') != -1) options = options | RegexOptions.ExplicitCapture;

			return new Regex(m_pattern, options);
		}

		#region IEquatable<BsonRegex> Members

		public override int GetHashCode()
		{
			return this.m_pattern.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			return this.Equals(obj as BsonRegex);
		}

		public bool Equals(BsonRegex other)
		{
			if (other == null)
				return false;
			if (ReferenceEquals(this, other))
				return true;

			return this.m_pattern == other.m_pattern && this.m_optionsString == other.m_optionsString;
		}

		#endregion

		public static bool operator ==(BsonRegex a, BsonRegex b)
		{
			if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
				return false;
			else if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
				return false;
			else
				return a.Equals(b);

		}
		public static bool operator !=(BsonRegex a, BsonRegex b)
		{
			return !(a == b);
		}

		public override string ToString()
		{
			return string.Format("/{0}/{1}", m_pattern, m_optionsString);
		}
	}
}
